from turtle import Turtle, Screen, mainloop, Vec2D
import math
import random

"""
Ref:
    * The Nature of Code: NOC_1_10_motion_101_acceleration_array
"""


class PVec2D(Vec2D):
    def setMag(self, val):
        v = math.sqrt(self[0]**2 + self[1]**2)
        v0 = val/(v+0.001)*self[0]
        v1 = val/(v+0.001)*self[1]
        return PVec2D(v0, v1)

    def limit(self, val):
        v = math.sqrt(self[0]**2 + self[1]**2)
        v0 = val/(v+0.001)*self[0]
        v1 = val/(v+0.001)*self[1]
        return PVec2D(v0, v1)

    def add(self, vec):
        self[0] = self[0] + vec[0]
        self[1] = self[1] + vec[1]

    def __add__(self, other):
        return PVec2D(self[0]+other[0], self[1]+other[1])

    def __sub__(self, other):
        return PVec2D(self[0]-other[0], self[1]-other[1])

class Mover(Turtle):
    def __init__(self, screen):
        Turtle.__init__(self)

        self.shape("circle")
        self.shapesize(2)

        cl = int(random.random()*127)
        self.color((0, 0, 0), (cl, cl, cl))

        self.screen = screen
        self.scree_size = screen.screensize()

        self.position_  = PVec2D(self.scree_size[0]*random.random(),
                                 self.scree_size[1]*random.random())
        self.mouse_pos_ = PVec2D(0, 0)

        self.velocity_ = PVec2D(0, 0)
        self.topspeed_ = 5

        self.penup()
        self.setpos(self.position_[0], self.position_[1])

    def handle_move(self, x, y):
        self.mouse_pos_ = PVec2D(x, y)

    def handle_timer(self):
        acc = self.mouse_pos_ - self.position_
        acc = acc.setMag(1)

        self.velocity_ = self.velocity_ + acc
        self.velocity_ = self.velocity_.limit(self.topspeed_)

        self.position_ = self.position_ + self.velocity_
        self.setpos(self.position_[0], self.position_[1])

def draw():
    screen = Screen()
    screen.setup(600, 400)
    screen.colormode(255)
    screen.tracer(4, 0)

    arrM = []
    for i in range(20):
        m = Mover(screen)
        m.speed("fastest")
        arrM.append(m)

    def onmove(obj, fun, add=None):
        if fun is None:
            obj.cv.unbind('<Motion>')
        else:
            def eventfun(event):
                fun(obj.cv.canvasx(event.x) / obj.xscale, -obj.cv.canvasy(event.y) / obj.yscale)
            obj.cv.bind('<Motion>', eventfun, add)

    def handle_move(x, y):
        for m in arrM: m.handle_move(x, y)

    def handle_timer():
        for m in arrM: m.handle_timer()
        screen.ontimer(handle_timer, 1)

    onmove(screen, handle_move)
    screen.ontimer(handle_timer, 1)

if __name__ == "__main__":
    draw()

    mainloop()
